//table operator
// fulfills the role of a dealer at a blackjack table
// changes the game model according to players' requests and the rules of the game

package sbcc.edu.blackjack.server;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import sbcc.edu.blackjack.GameState;
import sbcc.edu.blackjack.Player;
import sbcc.edu.blackjack.Table;
import sbcc.edu.blackjack.client.ClientTable;
import sbcc.edu.blackjack.client.DummyPlayer;
import sbcc.edu.blackjack.command.client.GiveTurnCommand;
import sbcc.edu.blackjack.command.client.SyncTableCommand;
import sbcc.edu.blackjack.command.table.TableCommand;

public class TableOperator implements Runnable {
	public Table table;
	int MAX_PLAYERS = 5;
	private int MAX_HAND_SIZE = 5;
	private Player dealer;		//FIXME dealer should be on the table with the rest of the players! part of the model
	private GameState state;
	Queue<TableCommand> history; // send to clients to synchronize their state
									// if joining mid-game
	private boolean stop;
	private DummyPlayer[] dummyPlayers;
	private int dummyIndex;
	private Player[] players;

	public TableOperator() {
		table = new Table();
		dealer = new Player(null);
		state = GameState.STOPPED;
		stop = false;
	}

	public void run() {
		while (!stop) {
			history = new ConcurrentLinkedQueue<TableCommand>(); // reset the
																	// synchronization
																	// queue
			if (state == GameState.STOPPED) {
				// clear cards from players
				for (Player cur : table.getAllPlayers()) {
					if (cur != null) {
						cur.getHand().clear();
					}
				}
				// shuffle shoe
				table.getShoe().shuffle();
				state = GameState.BETTING;
				// set all connected players state to BETTING
				for (Player cur : table.getAllPlayers()) {
					if (cur != null) {
						cur.setState(GameState.BETTING);
					}
				}
			}

			// wait for players to receive BET commands
			if (state == GameState.BETTING) {
				long startTime = System.currentTimeMillis();
				while (System.currentTimeMillis() < startTime + 30000) { // wait
																			// thirty
																			// seconds
																			// TODO
																			// if
																			// all
																			// players
																			// are
																			// done
																			// betting
					for (Player cur : table.getAllPlayers()) {
						TableCommand command = null;
						if (cur != null)
							command = cur.getTableCommand();
						if (command != null) {
							command.setPlayer(cur);
							command.setTable(table);
							command.execute();
						}
					}
				}
				state = GameState.PLAYING;
			}
			if (state == GameState.PLAYING) {
				// TODO begin by syncing the table, so that players see each
				// others' bets
				players = table.getAllPlayers();
				// deal first card to each player in sequence
				for (Player cur : players) {
					if (cur != null) {
						cur.addCard(table.getShoe().drawCard());
					}
				}
				// deal first card to dealer
				dealer.addCard(table.getShoe().drawCard());
				// deal second card to each player in sequence
				for (Player cur : players) {
					if (cur != null) {
						cur.addCard(table.getShoe().drawCard());
					}
				}
				// deal second card to dealer
				dealer.addCard(table.getShoe().drawCard());

				// sync the table once all the cards are dealt
				dummyIndex = 0;
				dummyPlayers = new DummyPlayer[players.length];
				for (Player cur : players) {
					if (cur != null) {
						dummyPlayers[dummyIndex++] = new DummyPlayer(cur.getId(), cur.getState(), cur.getBet(), cur.getHand());
					}
					else {
						dummyPlayers[dummyIndex++] = null;
					}
				}
				ClientTable t = new ClientTable(dummyPlayers, state, table.getId());
				new SyncTableCommand(t);
				

				// give each player a turn
				for (int i = 0; i < table.getAllPlayers().length; i++) {
					// set connected players state to PLAYING ONE BY ONE

					if (table.getAllPlayers()[i] != null) {
						table.getAllPlayers()[i].setState(GameState.PLAYING);
						// while client has an available move
						while (isPlaying(table.getAllPlayers()[i])) {
							// send GiveTurn command to client
							table.getAllPlayers()[i]
									.sendCommand(new GiveTurnCommand());
							// wait until they respond or timeout
							TableCommand play;
							while ((play = table.getAllPlayers()[i]
									.getTableCommand()) == null) {
								// busy wait, should have a time counting by
								// here
							}
							// execute the command
							play.setTable(table);
							play.setPlayer(table.getAllPlayers()[i]);
							play.execute();
							updateClients(play);
						}
					}
				}
				// well need to take into account players who just joined the
				// game.
				// play dealer's hand here
				state = GameState.STOPPED;
			}
		}
	}

	public void stop() {
		stop = true;
	}

	private boolean isPlaying(Player cur) {
		// TODONE determine whether the player can make a move
		if (cur.numCards() < MAX_HAND_SIZE) {
			if (cur.handTotal() < 21) {
				if (cur.getState() == GameState.PLAYING) {
					return true;
				}
			}
		}
		return false;
	}

	public boolean isTableFull() {
		if (table.numPlayers() == table.MAX_PLAYERS)
			return true;
		else
			return false;
	}

	public synchronized void addPlayer(Player p) {
		// TODO something else, probably
		table.addPlayer(p);
	}

	public GameState getState() {
		return state;
	}

	public void setState(GameState s) {
		state = s;
	}

	private void updateClients(TableCommand c) {
		for (Player p : table.getAllPlayers()) {
			p.sendCommand(c);
		}
	}

	public int getId() {
		return table.getId();
	}

	public void removePlayer(Player p) {
		for(int i = 0; i < players.length; i++){
			if (players[i] == p){
				//set that player refrence to null
				players[i] = null;
			}
		}
	}

}
